/**
 * 基于浏览器内置SubtleCrypto的加密实现类
 */
import { HashAlgorithm, SubtleCryptoAlgorithmMap } from '../types';
import { StringHashOptions, ArrayBufferHashOptions, BlobHashOptions, AdapterInterface } from './AdapterInterface';
import { processBlobChunks } from '../../fs/helpers';

/**
 * SubtleCrypto实现类
 * 实现AdapterInterface接口，使用浏览器内置的SubtleCrypto API进行加密操作
 */
export class SubtleCryptoAdapter implements AdapterInterface {
  private __subtleCrypto: SubtleCrypto;

  /**
   * 验证算法是否支持
   * @param algorithm - 哈希算法
   * @throws 如果算法不支持则抛出异常
   */
  private __validateAlgorithm(algorithm: HashAlgorithm): void {
    // 检查是否使用MD5算法
    if (algorithm === HashAlgorithm.MD5) {
      throw new Error('SubtleCrypto API不支持MD5算法，请使用CryptoJS适配器');
    }
  }

  /**
   * 获取SubtleCrypto支持的算法名称
   * @param algorithm - 哈希算法
   * @returns SubtleCrypto支持的算法名称
   * @throws 如果算法不支持则抛出异常
   */
  private __getSubtleAlgorithm(algorithm: HashAlgorithm): string {
    // 映射我们的HashAlgorithm到SubtleCrypto支持的算法名称
    const algorithmMap: SubtleCryptoAlgorithmMap = {
      [HashAlgorithm.SHA1]: 'SHA-1',
      [HashAlgorithm.SHA256]: 'SHA-256',
      [HashAlgorithm.SHA512]: 'SHA-512'
    };

    const subtleAlgorithm = algorithmMap[algorithm];
    if (!subtleAlgorithm) {
      throw new Error(`不支持的哈希算法: ${algorithm}`);
    }

    return subtleAlgorithm;
  }

  /**
   * 构造函数
   * 初始化SubtleCrypto实例
   * @throws 如果浏览器不支持SubtleCrypto API则抛出异常
   */
  constructor() {
    if (!window.crypto || !window.crypto.subtle) {
      throw new Error('浏览器不支持SubtleCrypto API');
    }
    this.__subtleCrypto = window.crypto.subtle;
  }

  /**
   * 计算字符串哈希值
   * @param {StringHashOptions} options - 字符串哈希选项
   * @returns {Promise<string>} 哈希值（十六进制字符串）
   */
  async stringHash(options: StringHashOptions): Promise<string> {
    const { str, algorithm } = options;

    // 检查是否使用MD5算法
    this.__validateAlgorithm(algorithm);

    // 将字符串转换为ArrayBuffer
    const encoder = new TextEncoder();
    const data = encoder.encode(str);

    // 调用arrayBufferHash方法计算哈希
    return this.arrayBufferHash({ arrayBuffer: data.buffer, algorithm });
  }

  /**
   * 计算ArrayBuffer哈希值
   * @param {ArrayBufferHashOptions} options - ArrayBuffer哈希选项
   * @returns {Promise<string>} 哈希值（十六进制字符串）
   */
  async arrayBufferHash(options: ArrayBufferHashOptions): Promise<string> {
    const { arrayBuffer: data, algorithm } = options;

    // 检查是否使用MD5算法
    this.__validateAlgorithm(algorithm);

    // 获取SubtleCrypto支持的算法名称
    const subtleAlgorithm = this.__getSubtleAlgorithm(algorithm);

    try {
      // 创建哈希对象并计算哈希
      const hashBuffer = await this.__subtleCrypto.digest(subtleAlgorithm, data);

      // 将ArrayBuffer转换为十六进制字符串
      const hashArray = Array.from(new Uint8Array(hashBuffer));
      return hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
    } catch (error) {
      throw new Error(`计算哈希时出错: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 计算Blob对象哈希值
   * @param {BlobHashOptions} options - Blob哈希选项
   * @returns {Promise<string>} 哈希值（十六进制字符串）
   * @description 对于大文件，会自动分块处理并支持进度回调
   */
  async blobHash(options: BlobHashOptions): Promise<string> {
    const { blob, algorithm, chunkSize = 1024 * 1024, onProgress } = options;

    // 检查是否使用MD5算法
    this.__validateAlgorithm(algorithm);

    // 获取SubtleCrypto支持的算法名称
    const subtleAlgorithm = this.__getSubtleAlgorithm(algorithm);

    // 这里我们使用一个ArrayBuffer来累积所有数据
    const fileSize = blob.size;
    try {
      const resultArray = new Uint8Array(fileSize);
      await processBlobChunks({
        blob,
        chunkSize,
        onChunk: async (chunkArrayBuffer: ArrayBuffer, offset) => {
          const chunkArray = new Uint8Array(chunkArrayBuffer);
          resultArray.set(chunkArray, offset);

          // 更新进度
          if (onProgress) {
            const progress = Math.min(100, ((offset + chunkArray.length) / fileSize) * 100);
            onProgress(progress);
          }
        }
      });

      // 计算完整数据的哈希
      const hashBuffer = await this.__subtleCrypto.digest(subtleAlgorithm, resultArray.buffer);

      // 将ArrayBuffer转换为十六进制字符串
      const hashArray = Array.from(new Uint8Array(hashBuffer));
      return hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
    } catch (error) {
      throw new Error(`计算哈希时出错: ${error instanceof Error ? error.message : String(error)}，建议使用CryptoJS`);
    }
  }
}